--- title: "01-Redis 学习路线" created: 2025-11-25 tags: - 项目筑基 --- # Redis 学习路线 ## 介绍 很多同学第一次接触 Redis 可能都是因为 “缓存”,也有很多同学误以为 Redis 就是缓存、只能做缓存。 事实上,Redis 是知名的高性能内存 K / V 存储系统,除了缓存之外,Redis 还可以用作配置存储、消息队列、解决分布式一致性问题等。正因为 Redis 的高性能、通用性、易用性、功能强大,使得它成为了后端开发中必不可少的中间件。 只要你的学习方向是后端开发,就必须要系统地学习 Redis。不仅要学会应用到项目中,还要学习它优秀的系统设计以及实现原理,可以开拓我们开发程序、解决问题的思路。 ## 学习条件 1. 目标方向是后端开发或数据库、数据开发相关的岗位(前端同学先不要学了) 2. 先学完一套开发框架(比如 SSM、SpringBoot),再学习 Redis ## 学习路线 建议大家按照以下五个阶段来学习: 1. 基础入门 2. 实战应用 3. 高阶知识 4. 底层原理 5. 备战面试 ### 知识点 - 一、基础入门 - Redis 是什么? - SQL 和 NoSQL - Redis 特点 - Redis 安装 - 连接方式 - 常用命令 - 通用命令 - 命令手册 - 基本数据结构 - String - Hash - List - Set - SortedSet - Redis 管理工具 - Redis Insight - Quick Redis - RESP - Redis 客户端及用法 - Jedis - Spring Data Redis - Redisson - Lettuce - 二、实战应用 - 分布式 Session - 高级数据结构 - GEO(地理位置计算) - Bitmap(实现签到) - HyperLogLog(实现 U / V 统计) - Bloom Filter - 消息队列 - Pub/Sub - Stream - 缓存 - 缓存更新 - Cache Aside 模式 - Read / Write Through 模式 - Write Behind 模式 - 缓存问题及解决方案 - 缓存穿透 - 缓存击穿 - 缓存雪崩 - 多级缓存 - 缓存预热 - 分布式全局唯一 id 生成 - Lua 脚本(实现秒杀) - 分布式锁 Redisson 实现 - Feed 流实现 - 事务 - 三、高阶知识 - Redis 最佳实践 - 键值设计 - 客户端优化 - 服务端优化 - 数据持久化 - RDB - AOF - 两者对比 - 主从同步 - 全量同步 - 增量同步 - 主从集群优化 - 哨兵集群 - 介绍和特点 - 故障转移 - 分片集群 - 介绍和特点 - 散列插槽 - 集群伸缩 - 故障转移 - 数据迁移 - 四、底层原理 - 底层数据结构 - 动态字符串 - IntSet - Dict - ZipList - SkipList - Redis Object - 五种基本数据结构的底层 - Redis 网络模型 - 阻塞 I / O - 非阻塞 I / O - I / O 多路复用 - Redis 线程模型 - Redis 通信协议 - Redis 内存淘汰策略 - 五、备战面试 ## 学习资源 ### 1、入门教程(从这里开始) 直接看这套视频课程即可一条龙入门: 几乎包括了 Redis 所有入门知识和主流应用、还有高级用法和原理的讲解,强烈推荐。 除了视频学习之外,还可以配合《图解 Redis》文档食用:[https://xiaolincoding.com/redis/](https://xiaolincoding.com/redis) ### 2、项目实战 Redis 项目推荐及笔记: ### 3、可视化工具 Redis Insight:[https://redis.io/docs/stack/insight/](https://redis.io/docs/stack/insight) Quick Redis: ### 4、命令手册 Redis 官网命令集: (命令忘了就查) 中文版命令集: ### 5、经典面试题 视频 1:[https://www.bilibili.com/video/BV1Ni4y1Q7XM/](https://www.bilibili.com/video/BV1Ni4y1Q7XM) 视频 2: 视频 3: 常见面试题整理(by 小林): ## 学习建议 1. Redis 是一个注重实际运用的技术,在学习 Redis 的过程中,要记得多敲命令 / 多写代码来操作 Redis,将 Redis 实际运用到你之前做过的项目中,而不是死记硬背。尤其是不要去背命令和代码,忘了就查、多写几次后印象自然就深刻了。 2. 对于初学后端的同学来说,学完第二阶段(实战应用)后就可以再去学消息队列、微服务等其他知识了。等主流的后端开发技术都会用后、面试前再回过头来补高级知识和原理即可。 3. Redis 的最佳实践部分要重点学习,建议是整理笔记便于自己复习查阅。争取养成好的设计和编码习惯,对以后的工作发展会很有帮助。 ## 实操干货:把高频命令敲一遍 上面路线列的是"学什么",这一节补"怎么用"——都是我实际用得最多的东西,忘了就回来查。 ### 连接与通用命令 ```bash # 本机默认 6379 直接进;远程加 -h -p,有密码进交互后 AUTH 输入(-a 会把密码留在 shell 历史里) redis-cli redis-cli -h 127.0.0.1 -p 6379 # 通用命令:跟 key 本身打交道,跟类型无关 SET name "redis" # 写 GET name # 读 EXISTS name # 判断是否存在,返回 1/0 DEL name # 删除 TYPE name # 看类型:string / hash / list / set / zset KEYS * # 列出所有 key——只在玩具库用!生产是 O(N) 全库遍历,会卡死 SCAN 0 MATCH user:* COUNT 100 # 生产用 SCAN 渐进遍历:游标从 0 开始,返回 0 才是遍历完 DBSIZE # key 总数 FLUSHDB # 清空当前库——生产环境远离这条命令 ``` > 💡 Redis 默认 16 个库(0~15),`SELECT 1` 切换。但注意:集群模式下只有 db0 一个库,别把业务隔离寄托在多库上。 ### 五大类型:命令实操 **String —— 缓存、计数、分布式锁的底座**: ```bash SET user:1:name "张三" SET counter 100 INCR counter # 原子自增:阅读量、限流计数都靠它 INCRBY counter 50 # 自增 50 SET lock:order:1 "uuid-xxx" NX EX 10 # 不存在才写 + 10 秒过期:最简版分布式锁 MSET k1 v1 k2 v2 # 批量写 MGET k1 k2 # 批量读,省 N 次网络往返 ``` **Hash —— 对象缓存首选,比整个对象 JSON 序列化进 String 好改单个字段**: ```bash HSET user:1 name "张三" age 21 level 3 # 多个 field 一次写 HGET user:1 name HGETALL user:1 # 全取(field 上千的大对象慎用,改用 HMGET 指定取) HDEL user:1 level HEXISTS user:1 age # 判断 field 是否存在 ``` **List —— 消息队列、最新列表**: ```bash LPUSH news:latest "a" "b" "c" # 左进 LRANGE news:latest 0 4 # 取前 5 条;-1 表示末尾 RPOP news:latest # 右出(LPUSH + RPOP 就是一个简单队列) LLEN news:latest # 长度 BRPOP news:latest 5 # 阻塞式右出:5 秒内没数据就等着,比轮询 RPOP 优雅 ``` **Set —— 去重、抽奖、共同关注**: ```bash SADD lottery "张三" "李四" "王五" SISMEMBER lottery "张三" # 是否在里面(去重判断) SRANDMEMBER lottery 2 # 随机看 2 人(不取出);SPOP 是抽出即移除 SCARD lottery # 元素个数 SINTER follow:a follow:b # 交集:共同关注 SUNION follow:a follow:b # 并集 SDIFF follow:a follow:b # 差集:a 关注了但 b 没关注 ``` **ZSet —— 排行榜,直接按分数排好序**: ```bash ZADD rank 95 "张三" 87 "李四" 92 "王五" ZINCRBY rank 5 "李四" # 加分 ZREVRANGE rank 0 9 WITHSCORES # 前 10 名(分数从高到低) ZREVRANK rank "李四" # 某人排第几 ZRANGEBYSCORE rank 90 100 # 取 90~100 分区间 ZCARD rank # 总人数 ``` ### TTL 与过期 ```bash EXPIRE user:1:name 60 # 60 秒后过期 TTL user:1:name # 还剩几秒;-1 永不过期,-2 key 已不存在 PERSIST user:1:name # 取消过期 SET code "6666" EX 300 NX # 写入时直接带过期 + 不存在才写:验证码标准写法 ``` 两个我踩过的坑: - 对一个已有 TTL 的 key 再执行 `SET`,**过期时间会被清掉**变回永久 key。想保住 TTL 要用 `SET key value KEEPTTL`(Redis 6.0+),或者重新 EXPIRE 一次。 - 过期是"惰性删除 + 定期抽样删除"的组合:key 到期不一定立刻物理消失,但 GET 不到了。内存吃紧时靠淘汰策略兜底(maxmemory-policy,常用 allkeys-lru)。 ## 缓存三兄弟:穿透 / 击穿 / 雪崩 三个问题的共同点是"缓存不在了,请求全砸到数据库",但成因不同,解法也不同: | 问题 | 成因 | 解法 | | --- | --- | --- | | 缓存穿透 | 查**根本不存在**的数据,缓存永远不命中 | 缓存空值(短 TTL)+ 布隆过滤器兜底 + 入参合法性校验 | | 缓存击穿 | 一个**热点 key 过期**的瞬间,海量请求直击数据库 | 互斥锁(只放一个请求去回源)+ 热点数据用逻辑过期不真删 | | 缓存雪崩 | **大批 key 同时过期**(或 Redis 宕机),数据库被瞬间打爆 | 过期时间加随机值打散 + 多级缓存 + 集群高可用 + 限流降级 | 配合 Cache Aside 模式的标准读法: ```python import json import random def get_user(uid): key = f"user:{uid}" val = r.get(key) if val is not None: return json.loads(val) # 缓存没有 → 查库 user = db.query(User).get(uid) if user is None: # 穿透防护:空结果也缓存,短 TTL,挡住恶意 id 反复打库 r.set(key, "", ex=60) return None # 雪崩防护:过期时间加随机,避免同批 key 一起死 r.set(key, json.dumps(user), ex=3600 + random.randint(0, 300)) return user ``` 写路径一句话:**先更新数据库,再删缓存**(不是更新缓存)——删除让下次读自然回源,避免并发写把旧值写回缓存。这就是 Cache Aside 模式的全部。 ## 持久化:RDB vs AOF 速记 | | RDB | AOF | | --- | --- | --- | | 记什么 | 某一时刻的**全量快照**(二进制) | **每条写命令**追加进日志 | | 触发 | save(阻塞主线程,别用)/ bgsave(fork 子进程) | appendfsync always / everysec / no | | 恢复速度 | 快(直接载入二进制) | 慢(逐条重放命令) | | 数据安全 | 两次快照之间的数据会丢 | everysec 最多丢 1 秒 | | 代价 | fork 瞬间可能卡顿 | 文件大,需要重写机制(bgrewriteaof)压缩 | 两者不是二选一:生产标配是 **RDB + AOF 混合持久化**(Redis 4.0+,`aof-use-rdb-preamble yes`)——恢复用 RDB 打底保证速度,丢数据按 AOF 标准算保证安全。 ## Python 实战:redis-py 三板斧 ```bash pip install redis ``` **连接:decode_responses=True 是第一个坑**: ```python import redis # 不加 decode_responses=True,拿到的全是 bytes,每个都要自己 .decode() r = redis.Redis(host="localhost", port=6379, db=0, decode_responses=True) # 服务里建议显式用连接池 pool = redis.ConnectionPool(host="localhost", port=6379, max_connections=50, decode_responses=True) r = redis.Redis(connection_pool=pool) ``` **日常操作直接对应命令名**: ```python r.set("user:1:name", "张三", ex=3600) # 命令叫 SET,方法就是 r.set,参数同理 name = r.get("user:1:name") r.hset("user:1", mapping={"name": "张三", "age": 21}) # Hash 批量写 user = r.hgetall("user:1") # 返回 dict r.incr("counter") # 原子自增 r.expire("user:1", 60) # 补 TTL r.exists("user:1") # 判断 r.delete("user:1") # 删除 ``` **Pipeline:把 N 次往返合成 1 次**: ```python # 循环里一条条 set,1000 个 key 就是 1000 次网络往返——很慢 pipe = r.pipeline() for i in range(1000): pipe.set(f"k:{i}", i, ex=3600) pipe.execute() # 一次性打包发送,快一个数量级 ``` > 💡 简易分布式锁的坑:`r.set(lock_key, token, nx=True, ex=10)` 这一行里 NX 和 EX 必须在同一条命令里写。先 SETNX 再 EXPIRE 是两步,中间进程一挂,锁就永远不过期——经典事故。 ## 进阶数据类型:Bitmap / HyperLogLog / GEO 路线篇"实战应用"阶段列的三个高级结构,补上命令体感——它们的共同思路是**用极小的内存解决特定统计问题**。 **Bitmap —— 签到打卡**: ```bash SETBIT sign:user:1:202608 14 1 # 用户 1 在 8 月第 15 天签到(位从 0 数起) GETBIT sign:user:1:202608 14 # 查某天是否签到 BITCOUNT sign:user:1:202608 # 本月签到总天数 ``` 一年签到 = 365 bit ≈ 46 字节。一亿用户的签到也就几百 MB——这是"按位存布尔"的威力。连续签到的判断配合 BITPOS 找第一个 0。 **HyperLogLog —— 海量去重计数(UV 统计)**: ```bash PFADD uv:20260831 "user:1" "user:2" "user:3" PFCOUNT uv:20260831 # 去重计数,标准误差约 0.81% PFMERGE uv:202608 uv:20260829 uv:20260830 # 合并出整月 UV ``` 固定约 **12KB** 内存就能数亿级 UV,还能任意合并。代价是:拿不到名单,只知道"大概有多少个不重复的"——**要数就别用 Set 存全量**,这是书里反复强调的取舍。 **GEO —— 附近的人 / 附近的店**: ```bash GEOADD shops 121.47 31.23 "门店A" 121.48 31.24 "门店B" GEOSEARCH shops FROMLONLAT 121.47 31.23 BYRADIUS 2000 m ASC COUNT 5 # 2km 内最近 5 家 ``` > 💡 GEO 底层就是 ZSet(经纬度编码进了 score)——所以 ZSet 那套 ZRANGEBYSCORE 的思路对它部分通用,理解成本低。 ## 消息队列三方案:List / Pub/Sub / Stream | 方案 | 命令 | 优点 | 硬伤 | | --- | --- | --- | --- | | List 队列 | LPUSH + BRPOP | 简单够用 | **无 ack**:消费者取走就没了,处理崩溃消息即丢 | | Pub/Sub | PUBLISH / SUBSCRIBE | 实时广播多订阅者 | **不持久化**:订阅者掉线期间的消息全丢 | | Stream | XADD / XREADGROUP / XACK | 消费组 + ack + 持久化,像简化版 Kafka | 略复杂,功能仍不及专业 MQ | Stream 消费组的最小骨架(消息不丢的关键是 XACK): ```bash XADD orders * item "book" price 59 # * 表示让 Redis 自动生成消息 ID XGROUP CREATE orders g1 0 # 建消费组 g1,从 0 开始消费 XREADGROUP GROUP g1 consumer1 COUNT 10 BLOCK 5000 STREAMS orders > XACK orders g1 1693-0 # 处理完确认;没 ack 的留在 PEL 里可重投 ``` 我的记忆锚点:**要丢得起就用 List/Pub-Sub(简单),丢不起就上 Stream(ack 兜底)**;再往上要求事务消息、堆积管理,那是专业 MQ(RocketMQ/Kafka)的地盘,别硬用 Redis 扛。 ## 高可用速记:主从 / 哨兵 / 分片集群 | 形态 | 解决什么 | 关键点 | | --- | --- | --- | | 主从复制 | 读扩容 + 数据热备 | 首次全量同步(bgsave RDB 传从库)+ 之后增量(repl_backlog 环形缓冲);**写永远走主** | | 哨兵集群 | 主挂了自动切换 | 监控 + 主观/客观下线判定 + Raft 式选主 + 通知客户端新主地址 | | 分片集群 | 写扩容 + 海量数据 | 16384 个哈希槽,按 key 的 CRC16 取模分槽;水平扩容=迁移槽 | 三层各管一件事:**主从解决"读",哨兵解决"高可用",分片解决"写和数据量"**。设计时按这个顺序自查需求:读压力大→加从库;怕单点→上哨兵;写也扛不住/数据太大→分片集群。 --- ⬅️ [[00-NoSQL 数据库总览|00-NoSQL 数据库总览]] 🏠 [[00-数据库|00-数据库]] ➡️ [[02-Redis 核心数据结构与命令|02-Redis 核心数据结构与命令]]